# coding=UTF-8
""" === Windows stdio ===
@author ideawu@163.com
@link http://www.ideawu.net/
Windows 版本的 Python, select() 函数只能接受 socket,不接受 File Object, 所以不能 select 标准输入输出.

为此, 我开发了 win_stdio, 让 select 也能对 stdin/stdout 进行操作. 核心思想就是用socket作为 stdin/stdout 的代理. 结构示意如下:
stdin -> stdin_sock -> my_stdin
stdout <- stdout_sock <- my_stdout


my_stdin 和 my_stdout 是 socket, 所以它们的 fileno 就可以用于 select 了.

首先一个线程读取 stdin, 写入 stdin_sock:
while True:
	data = os.read(sys.stdin.fileno(), 1024)
	if not data:
		break
	stdin_sock.send(data)


另一个线程读取 stdout_sock, 写入 stdout:
fd = sys.stdout.fileno()
while True:
	data = my_stdout.recv(1024)
	if not data:
		break
	os.write(sys.stdout.fileno(), data)


win_stdio 的一个实际例子如下:
from stdio import stdio
stdio.write('hello world')
data = stdio.read()
print stdio.STDIN_FILENO
print stdio.STDOUT_FILENO

File objects on Windows are not acceptable for select(),
this module creates two sockets: stdio.s_in and stdio.s_out,
as pseudo stdin and stdout.

@example
from stdio import stdio
stdio.write('hello world')
data = stdio.read()
print stdio.STDIN_FILENO
print stdio.STDOUT_FILENO
"""
import thread
import sys, os
import socket

# socket read/write in multiple threads may cause unexpected behaviors
# so use two separated sockets for stdin and stdout


def _create_stdio():
	def stdin_thread(sock, console):
		"""	read data from stdin, and write the data to sock
		"""
		try:
			fd = sys.stdin.fileno()
			while True:
				# DO NOT use sys.stdin.read(), it is buffered
				data = os.read(fd, 1024)
				#print 'stdin read: ' + repr(data)
				if not data:
					break
				while True:
					nleft = len(data)
					nleft -= sock.send(data)
					if nleft == 0:
						break
		except:
			pass
		#print 'stdin_thread exit'
		sock.close()

	def stdout_thread(sock, console):
		"""	read data from sock, and write to stdout
		"""
		try:
			fd = sys.stdout.fileno()
			while True:
				data = sock.recv(1024)
				#print 'stdio_sock recv: ' + repr(data)
				if not data:
					break
				while True:
					nleft = len(data)
					nleft -= os.write(fd, data)
					if nleft == 0:
						break
		except:
			pass
		#print 'stdin_thread exit'
		sock.close()


	class Console:
		def __init__(self):
			self.serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
			self.serv.bind(('127.0.0.1', 0))
			self.serv.listen(5)
			port = self.serv.getsockname()[1]

			# data read from stdin will write to this socket
			self.stdin_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
			self.stdin_sock.connect(('127.0.0.1', port))
			self.s_in, addr = self.serv.accept()

			# data read from this socket will write to stdout
			self.stdout_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
			self.stdout_sock.connect(('127.0.0.1', port))
			self.s_out, addr = self.serv.accept()

			self.STDIN_FILENO = self.s_in.fileno()
			self.STDOUT_FILENO = self.s_out.fileno()

			thread.start_new_thread(stdin_thread, (self.stdin_sock, self))
			thread.start_new_thread(stdout_thread, (self.stdout_sock, self))

		def close(self):
			self.s_in.close()
			self.s_out.close()
			self.stdin_sock.close()
			self.stdout_sock.close()
			self.serv.close()

		def write(self, data):
			return self.s_out.send(data)

		def read(self):
			data = self.s_in.recv(4096)
			return data

		# TODO: readline

	"""
	"""
	stdio = Console()
	return stdio


if __name__ == '__main__':
    stdio = _create_stdio()
    stdio.write('hello world')
    data = stdio.read()
    print stdio.STDIN_FILENO
    print stdio.STDOUT_FILENO